package cn.shop.cloud.redis;

import com.alibaba.fastjson.JSON;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.HashMap;
import java.util.Map;

/**
 * @program: shop-parent
 * @ClassName MyRedisTemplate
 * @description:
 * @author: Mokio
 * @create: 2024−12-23 17:22
 * @Version 2024 2.2 ; jdk1.8.0_181
 **/
// 模板类
public class MyRedisTemplate {
    //使用静态代码初始化连接池
    /*
    我们为什么需要在这里配置连接池！！！！！不单单是嫌麻烦，
    在该模版类中要想重写命令方法必须规定连接池，因为每一个方法都需要一个连接实例来执行命令
    要不就必须在创建模版实例时传进来一个连接池，这样的话就需要在模版类中写有参构造方法来接收传进来的连接池，如下所示：
    @Component
    public class MyRedisTemplate {
        //声明连接池
        private final JedisPool jedisPool;

        构造方法接收连接池对象参数
        @Autowired
        public MyRedisTemplate(JedisPool jedisPool) {
            this.jedisPool = jedisPool;
        }

       // 其他方法保持不变
   }
     */
    /*
    JedisPool 的作用：
    1、JedisPool 是 Jedis 库提供的一个连接池实现，它负责管理和分配 Redis 连接。
    2、通过 JedisPool，应用程序可以在需要时从池中获取一个 Jedis 实例
    （即 Redis 连接），并在使用完毕后将其归还到池中，而不是每次都创建新的连接。
    3、JedisPool 提供了配置连接池参数的功能，如最大空闲连接数、最大连接数、
    最小空闲连接数等，以满足不同的应用场景需求。
     */
    private static JedisPool jedisPool;

    static {
        // 配置对象
        JedisPoolConfig config = new JedisPoolConfig();

        // 配置连接池参数
        // 最大空闲连接数
        config.setMaxIdle(500);
        // 最大连接数
        config.setMaxTotal(500);
        // 最小空闲连接数
        config.setMinIdle(100);

        // 创建连接池
        jedisPool = new JedisPool(config, "localhost", 6379,100000);

    }

    //根据需要重新设计Redis各种方法，添加缓存时间和唯一前缀来便利业务处理

    //数据存储，需要缓存时间和唯一前缀，key和要存储的数据
    public <T> void set(KeyPrefix keyPrefix,String key,T data){

        // 声明redis连接对象
        Jedis jedis=null;

        try{
            //得到 redis连接对象
            jedis= jedisPool.getResource();
            //获取到真正的key
            String realKey= keyPrefix.getPrefix()+key;
            //判断是否需要设置过期时间
            if(keyPrefix.getExpireSeconds()>0){
                //延时
                /*
                注意两个点，
                一个是设置延时时间的话要用setex，否则就是set
                第二是要想往redis中存数据，要将数据序列化，使用JSON.toJSONString将数据转为JSON字符串
                 */
                jedis.setex(realKey, keyPrefix.getExpireSeconds(), JSON.toJSONString(data));
            }else{
                //新的数据
                jedis.set(realKey,JSON.toJSONString(data));
            }

        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            jedis.close();
        }
    }



    //获取数据，需要唯一前缀和key，和要取出的数据的类型
    public <T> T get(KeyPrefix keyPrefix,String key,Class<T> clazz){
        //得到 redis连接对象
        Jedis jedis=null;

        try{
            jedis= jedisPool.getResource();

            //获取到真正的key
            String realKey= keyPrefix.getPrefix()+key;

            String s = jedis.get(realKey);

            if(StringUtils.isEmpty(s)){
                //空的
                return  null;
            }
            return JSON.parseObject(s,clazz);

            //  set key value   get key ---> value

        }catch (Exception ex){
            ex.printStackTrace();
            // throw  new RuntimeException(ex.getMessage());
        }finally {
            jedis.close();
        }

        return null;
    }


    /**
     * 判断数据是否存在
     * @param keyPrefix
     * @param key
     * @return
     */
    public boolean exists(KeyPrefix keyPrefix,String key){
        //这里使用了try-with-resource，自动关闭资源
        try(Jedis jedis = jedisPool.getResource();){
            String realKey= keyPrefix.getPrefix()+key;
            return  jedis.exists(realKey);
        }catch (Exception ex){
            ex.printStackTrace();
            throw  new RuntimeException(ex.getMessage());
        }

        // return null;
    }


    // hash存储，需要唯一前缀，key，field（hashKey），和要存储的数据
    public <T> void hset(KeyPrefix keyPrefix,String key,String field,T data){

        try(Jedis jedis = jedisPool.getResource();){
            String realKey= keyPrefix.getPrefix()+key;
            //数据存储
            jedis.hset(realKey,field,JSON.toJSONString(data));

        }catch (Exception ex){
            ex.printStackTrace();
            throw  new RuntimeException(ex.getMessage());
        }

    }


    //hash获取指定数据，需要唯一前缀，key，field（hashKey），和要取出的数据类型
    public <T> T hget(KeyPrefix keyPrefix,String key,String field,Class<T> clazz){

        try(Jedis jedis = jedisPool.getResource();){
            String realKey= keyPrefix.getPrefix()+key;
            //数据存储
            String obj = jedis.hget(realKey, field);

            if(obj==null){
                return  null;
            }
            //拿到数据
            return   JSON.parseObject(obj,clazz);


        }catch (Exception ex){
            ex.printStackTrace();
            throw  new RuntimeException(ex.getMessage());
        }

    }


    //hash获取key下所有数据，需要唯一前缀，key，和要取出的数据类型
    public <T> Map<String,T> hgetAll(KeyPrefix keyPrefix, String key, Class<T> clazz){

        try(Jedis jedis = jedisPool.getResource();){
            String realKey= keyPrefix.getPrefix()+key;
            //数据存储

            //这里是先根据key将Redis中的数据取出放到map中
            Map<String, String> map = jedis.hgetAll(realKey);

            //判空
            if(map==null){
                return  null;
            }

            //创建一个新的resultMap，注意泛型不同
            Map<String,T> resultMap = new HashMap<>();

            //遍历map，将map中的数据转为对象并存入resultMap中
            for(Map.Entry<String,String> entry:map.entrySet() ){
                resultMap.put(entry.getKey(),JSON.parseObject(entry.getValue(),clazz));
            }

            return  resultMap;
        }catch (Exception ex){
            ex.printStackTrace();
            throw  new RuntimeException(ex.getMessage());
        }

    }

    //自增，需要唯一前缀和key
    public Long incr(KeyPrefix keyPrefix, String key){
        try(Jedis jedis = jedisPool.getResource();){
            String realKey= keyPrefix.getPrefix()+key;

            //使用自带方法自增
            return jedis.incr(realKey);
        }catch (Exception ex){
            ex.printStackTrace();
            throw  new RuntimeException(ex.getMessage());
        }
    }

    //自减，需要唯一前缀和key
    public Long decr(KeyPrefix keyPrefix, String key){
        try(Jedis jedis = jedisPool.getResource();){
            String realKey= keyPrefix.getPrefix()+key;

            //使用自带方法自减
            return jedis.decr(realKey);
        }catch (Exception ex){
            ex.printStackTrace();
            throw  new RuntimeException(ex.getMessage());
        }
    }

}
